/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package controle;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Weverton
 */
public class Minimax {

    private int dificuldade, profundidade = 0;

    public int[][] minimax(int[][] atual, int dificuldade) {
        this.dificuldade = dificuldade;
        int v = valorMax(atual, Integer.MIN_VALUE, Integer.MAX_VALUE);
        for (int[][] s : sucessores(atual, 3)) {
            atual = s;
            if (v == utilidade(s, 3)) {
                return s;
            }
        }
        return atual;
    }

    private int valorMax(int[][] atual, int a, int b) {
        if (testeFim(atual)) {
            return utilidade(atual, 3);
        }
        int v = Integer.MIN_VALUE;
        for (int[][] s : sucessores(atual, 3)) {
            profundidade++;
            v = max(v, valorMin(s, a, b));
            profundidade--;
            if (v >= b) {
                return v;
            }
            a = max(v, a);
        }
        return v;
    }

    private int valorMin(int[][] atual, int a, int b) {
        if (testeFim(atual)) {
            return utilidade(atual, 2);
        }
        int v = Integer.MAX_VALUE;
        for (int[][] s : sucessores(atual, 2)) {
            profundidade++;
            v = min(v, valorMax(s, a, b));
            profundidade--;
            if (v <= a) {
                return v;
            }
            b = min(v, b);
        }
        return v;
    }

    private int max(int v, int a) {
        if (v >= a) {
            return v;
        } else {
            return a;
        }
    }

    private int min(int v, int b) {
        if (v <= b) {
            return v;
        } else {
            return b;
        }
    }

    /*
     O teste de término irá variar conforme o nível de dificuldade escolhido pelo usuário.
     Nível Fácil: Até os estados da segunda geração de sucessores.
     Nível Médio: Até os estados da quarta geração de sucessores.
     Nível Difícil: Até os estados da sexta geração de sucessores.
     */
    private boolean testeFim(int[][] estado) {
        int contFim = 0;
        for (int i = 1; i < 12; i += 2) {
            for (int j = 1; j < 12; j += 2) {
                if (estado[i][j] == 4 || estado[i][j] == 5 || estado[i][j] == 6 || estado[i][j] == 7) {
                    contFim++;
                }
            }
        }
        if (contFim == 0) {
            return true;
        } else {
            switch (dificuldade) {
                case 1:
                    return profundidade == 2;
                case 2:
                    return profundidade == 4;
                case 3:
                    return profundidade == 6;
            }
        }
        return true;
    }

    /*
     Será calculada considerando os quadrados para completar. O MAX sempre terá
     o papel de jogador B. Portanto é vantajoso quando o quadrado contiver um dos
     valores 9 (quadrado completado pelo jogador B), 4 (quadrado para completar
     faltando 4 lados) e 6 (quadrado para completar faltando 2 lados). O valores
     6 e 4 sugerem que na sua vez de jogar o MAX terá a oportunidade de completar.
     O cálculo será feito da seguinte forma:
     (qtd.4) + (qtd.6) + (qtd.9) + (qtd.5) + (qtd.7) + (qtd.8)
     Onde qtd. = quantidade de quadrados com tais valores.
     */
    private int utilidade(int[][] estado, int vez) {
        int qtd4 = 0, qtd5 = 0, qtd6 = 0, qtd7 = 0, qtd8 = 0, qtd9 = 0;
        for (int i = 0; i <= 12; i++) {
            for (int j = 0; j <= 12; j++) {
                switch (estado[i][j]) {
                    case 4: {
                        qtd4 += 0;
                        break;
                    }
                    case 5: {
                        qtd5 += 0;
                        break;
                    }
                    case 6: {
                        qtd6 += 0;
                        break;
                    }
                    case 7: {
                        qtd7 += 0;
                        break;
                    }
                    case 8: {
                        if (vez == 2) {
                            qtd8 += 1;
                        } else {
                            qtd8 += 0;
                        }
                        break;
                    }
                    case 9: {
                        if (vez == 3) {
                            qtd9 += 1;
                        } else {
                            qtd9 += 0;
                        }
                        break;
                    }
                }
            }
        }
        return qtd4 + qtd5 + qtd6 + qtd7 + qtd8 + qtd9;
    }

    /*
     Serão gerados todos os estados a partir dos movimentos válidos. O estado
     inicial permite 84 estados válidos. No decorrer do jogo este valor é
     diminuído em um a cada jogada, mais os estados eliminados pelos critérios de poda.
     0 Um ponto do tabuleiro
     1 Uma ligação possível entre dois pontos
     2 Uma ligação feita entre dois pontos pelo jogador A
     3 Uma ligação feita entre dois pontos pelo jogador B = Computador.
     4 Um quadrado para completar faltando 4 lados
     5 Um quadrado para completar faltando 3 lados
     6 Um quadrado para completar faltando 2 lados
     7 Um quadrado para completar faltando 1 lado
     8 Um quadrado completado pelo jogador A
     9 Um quadrado completado pelo jogador B
     */
    private List<int[][]> sucessores(int[][] estado, int vez) {

        List<int[][]> listaSucessores = new ArrayList<>();

        for (int i = 0; i <= 12; i++) {
            for (int j = 0; j <= 12; j++) {
                if (estado[i][j] == 1) {
                    int[][] novoEstado = new int[13][13];
                    for (int k = 0; k <= 12; k++) {
                        for (int n = 0; n <= 12; n++) {
                            novoEstado[k][n] = estado[k][n];
                        }
                    }
                    if (vez == 3) {
                        novoEstado[i][j] = 3;
                    } else {
                        novoEstado[i][j] = 2;
                    }
                    // Linha horizontal
                    if (i % 2 == 0) {
                        if (i > 0) {
                            calcularMeio(novoEstado, (i - 1), j, vez);
                        }
                        if (i < 12) {
                            calcularMeio(novoEstado, (i + 1), j, vez);
                        }
                    } // Linha vertical
                    else if (j % 2 == 0) {
                        if (j > 0) {
                            calcularMeio(novoEstado, i, (j - 1), vez);
                        }
                        if (j < 12) {
                            calcularMeio(novoEstado, i, (j + 1), vez);
                        }
                    }
                    listaSucessores.add(novoEstado);
                }
            }
        }
        return listaSucessores;
    }

    private void calcularMeio(int[][] estado, int i, int j, int vez) {
        if (estado[i][j] < 7) {
            estado[i][j] += 1;
        } else if (estado[i][j] == 7) {
            if (vez == 3) {
                estado[i][j] = 9;
            } else {
                estado[i][j] = 8;
            }
        }
    }
}
